package BASEULTILS;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

class XMLManagements {
    /***
     * Nghiệp vụ đọc dữ liệu trong files XML có sẵn
     * @param lisField - Chứa danh sách các trường dữ liệu. Chỉ số = 0 dành cho Node Atribute Key.
     * @param elementNode - Phần tử Node con của Root Node. Ví dụ: <iMIC><Student></Student></iMIC> --> Root Node = "iMIC" & Element Node = "Student"
     * @param path - Chứa đường dẫn resource. Ví dụ: "D:imic/xml/Student.xml"
     * @return List<List<HashMap<String, String>>>
     * @throws Exception 
     */
    public List<List<HashMap<String, String>>> ReadElements(List<Object> lisField, 
                        String elementNode, String path)
                        throws Exception {
        // Khai báo & khởi tạo các đối tượng
        List<List<HashMap<String, String>>> lisHash = new ArrayList<List<HashMap<String, String>>>();
        List<HashMap<String, String>> lisTemp = new ArrayList<HashMap<String, String>>();
        try {
            // 1. Khởi tạo đối tượng files (tham chiếu tới files)
            File fXmlFile = new File(path);
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = null;

            // 2. Kiểm tra files trước khi bindding dữ liệu trong files XML vào
            // Documents
            if (fXmlFile.exists()) {
                doc = dBuilder.parse(fXmlFile);
                doc.getDocumentElement().normalize();
                // 3. Lấy ra danh sách phần tử "Student"
                NodeList nList = doc.getElementsByTagName(elementNode);

                // 4. Khai báo và khởi tạo HashMap
                HashMap<String, String> objHash = null;

                int iItems = nList.getLength();
                // 4. Duyệt danh sách phần tử trong nList
                String key = "";
                String value = "";
                for (int j = 0; j < iItems; j++) {
                    Node nNode = nList.item(j);
                    if (nNode.getNodeType() == Node.ELEMENT_NODE) {
                        Element eElement = (Element) nNode;
                        objHash = new HashMap<String, String>();

                        for (int k = 0; k < lisField.size(); k++) {
                            if (k == 0) {
                                // Identify atribute key - Id
                                key = "" + lisField.get(k);
                                value = "" + eElement.getAttribute("" + lisField.get(k));
                                objHash.put(key, value);
                            } else {
                                // Node items in Element Node
                                key = "" + lisField.get(k);
                                value = ""
                                        + eElement.getElementsByTagName("" + lisField.get(k)).item(0).getTextContent();
                                objHash.put(key, value);
                            }
                        }
                        // 5. Gắn thông tin vào List<HashMap<String, String>>
                        lisTemp.add(objHash);
                    }
                }
                // 6. Add các List<HashMap<String, String>> vào trong
                // List<List<HashMap<String, String>>>
                lisHash.add(lisTemp);
            } else {
                return null;
            }
        } catch (Exception e) {
            throw e;
        }
        return lisHash;
    }

    /**
     * Nghiệp vụ xử lý ghi dữ liệu xuống XML files
     * @param lisField - Chứa danh sách các trường dữ liệu. Chỉ số = 0 dành cho Node Atribute Key.
     * @param lisValues - Chứa danh sách dữ liệu tương ứng. Chỉ số = 0 chứa dữ liệu cho Node Atribute Key.
     * @param rootNode - Chứa tên node gốc (gắn trực tiếp vào Documents).
     * @param elementNode - Chứa tên node con (gắn trực tiếp vào node gốc).
     * @param path - Chứa đường dẫn resource. Ví dụ: "D:imic/xml/Student.xml"    
     * @param writeType - Kiểu ghi dữ liệu xuống files. writeType = true: Ghi mới và 
     * writeType = false: Ghi nối tiếp.     * 
     * @return true or false
     * @throws Exception
     */
    public boolean WriteElements(List<Object> lisField, 
                            List<List<Object>> lisValues, String rootNode,
                            String elementNode, String path, boolean writeType) throws Exception {
        try {
            File xmlFile = null;
            File objFile = null;
            if (writeType == false) {
                objFile = new File(path);
                /*
                 * TRUE: có tồn tại files FALSE: ko tồn tại files
                 */
                if (objFile.exists()) {
                    try {
                        xmlFile = new File(path);
                    } catch (Exception e) {
                        throw e;
                    }
                }
            }
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

            // Gốc XML
            Document doc = null;
            Element rootElement = null;
            Node rootNodeElement = null;
            // TH: Ghi mới ra files
            if (writeType) {
                doc = docBuilder.newDocument();
                rootElement = doc.createElement(rootNode); // phần tử gốc
                doc.appendChild(rootElement);
            } else {
                // TH: Ghi nối tiếp ra files
                if (xmlFile != null) {
                    doc = docBuilder.parse(xmlFile);
                    rootNodeElement = doc.getFirstChild();
                } else {
                    return false;
                }
            }

            // Duyệt danh sách và gắn các node vào docs
            for (int i = 0; i < lisValues.size(); i++) {
                Element childElement = doc.createElement(elementNode);
                // Kiểm tra kiểu ghi dữ liệu
                if (writeType)
                    rootElement.appendChild(childElement);
                else
                    rootNodeElement.appendChild(childElement);

                /* Duyệt danh sách các trường dữ liệu */
                for (int j = 0; j < lisValues.get(i).size(); j++) {
                    if (j == 0) {
                        // RollNo
                        Attr attr = doc.createAttribute("" + lisField.get(0));
                        attr.setValue("" + lisValues.get(i).get(j));
                        childElement.setAttributeNode(attr);
                    } else {
                        // ?
                        Element nodeName = doc.createElement("" + lisField.get(j));
                        nodeName.appendChild(doc.createTextNode("" + lisValues.get(i).get(j)));
                        childElement.appendChild(nodeName);
                    }
                }

            }

            // write the content into xml file
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            DOMSource source = new DOMSource(doc);

            StreamResult result = null;
            if (writeType) {
                File objfile = new File(path);
                result = new StreamResult(objfile);
            } else {
                result = new StreamResult(objFile.getPath());
            }
            // StreamResult result = new StreamResult("D:\\iMICDemo.xml");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.transform(source, result);
            // System.out.println("File saved!");

        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();
        } catch (TransformerException tfe) {
            tfe.printStackTrace();
        }
        return true;
    }

    /**
     * Nghiệp vụ cập nhật dữ liệu trong XML Files
     * @param lisField - Chứa danh sách các trường dữ liệu. Chỉ số = 0 dành cho Node Atribute Key.
     * @param lisValues - Chứa danh sách dữ liệu tương ứng. Chỉ số = 0 chứa dữ liệu cho Node Atribute Key.
     * @param resource - Chứa đường dẫn resource. Ví dụ: "D:imic/xml/"
     * @param fileName - Chưa tên files XML định nghĩa. Ví dụ: "Students.xml"
     * @param attributeKey - Thuộc tính key định danh cho mỗi ElementNode. Ví dụ: <Student id="1"> --> attributeKey = "id"
     * @param elementNode - Phần tử Node con của Root Node. Ví dụ: <iMIC><Student></Student></iMIC> --> Root Node = "iMIC" & Element Node = "Student"
     * @param lisStudent
     * @return
     * @throws Exception
     */
    public boolean UpdateElements(List<Object> lisField, 
                    List<Object> lisValues, String path, String attributeKey,
                    String elementNode) throws Exception {
        try {
            // Lấy đường dẫn tương đối
            boolean bFlag = false;
            File objFile = new File(path);
            File xmlFile = null;
            /*
             * TRUE: có tồn tại files FALSE: ko tồn tại files
             */
            if (objFile.exists()) {
                try {
                    xmlFile = new File(path);
                    DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
                    DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
                    Document doc = docBuilder.parse(xmlFile);

                    // Cập nhật thông tin Sinh Viên
                    NodeList nList = doc.getElementsByTagName(elementNode);
                    for (int i = 0; i < nList.getLength(); i++) {
                        // Nhận về thông tin student element bởi tag name
                        Node nChild = doc.getElementsByTagName(elementNode).item(i);
                        NamedNodeMap attr = nChild.getAttributes();
                        Node nodeAttr = attr.getNamedItem(attributeKey);

                        if (nodeAttr.getTextContent().equals("" + lisValues.get(0))) {
                            NodeList list = nChild.getChildNodes();
                            String str = nChild.getNodeName();

                            for (int j = 0; j < list.getLength(); j++) {
                                Node node = list.item(j);

                                for (int k = 0; k < lisField.size(); k++) {
                                    if (lisField.get(k).equals(node.getNodeName())) {
                                        node.setTextContent("" + lisValues.get(k));
                                    }
                                }
                            }
                        }
                    }

                    // write the content into xml file
                    TransformerFactory transformerFactory = TransformerFactory.newInstance();
                    Transformer transformer = transformerFactory.newTransformer();
                    DOMSource source = new DOMSource(doc);
                    StreamResult result = new StreamResult(xmlFile);
                    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                    transformer.transform(source, result);

                } catch (Exception ex) {
                    throw ex;
                }
            }
        } catch (Exception e) {
            throw e;
        }
        return true;
    }
}
